import { Intersection2D, Rect, v2, Vec2 } from "cc";
import { Barrier } from "../../LevelColler/Script/Barrier";
import EventManager from "../../Script/Common/EventManager";
import GlobalData from "../../Script/Common/GlobalData";
import GameConfig from "../../Script/GameSpecial/GameConfig";
import { EventType } from "../../Script/GameSpecial/GameEventType";
import { GlobalEnum } from "../../Script/GameSpecial/GlobalEnum";
import { Role } from "./Role";

const pi = 3.141592653;
const pi2 = 6.283185306;
const pi5 = 1.5707963265;

export default class AtkBox {

    /**攻击者 */
    protected atkRole: Role;
    protected radian: number;//角色朝向弧度
    protected sin: number;//角色朝向弧度的sin值
    protected cos: number;
    protected tan: number;
    //攻击框坐标
    protected position: Vec2;
    protected x: number;
    protected y: number;
    protected type: GlobalEnum.AtkScopeType;//扇形，矩形
    //碰撞包围盒
    protected _aabb;
    //扇形
    protected radius: number;//扇形半径
    protected radiusSqr: number;
    protected halfRadianScope: number;//扇形弧度范围的一半
    protected halfSin: number;
    protected halfCos: number;
    protected halfTan: number;
    /**圆弧数据,axis:圆弧与哪根轴相交，0表示没有相交轴，1为X正半轴，2为Y正半轴 */
    protected arc: { pointLeft: Vec2, pointRight: Vec2 };
    /**左侧边的方向判定边界向量 */
    protected bordLeftV: Vec2;
    /**右侧边的方向判定边界向量 */
    protected bordRightV: Vec2;
    //矩形
    protected halfWidth: number;//矩形宽度
    protected length: number;//矩形长度
    protected rect: Rect;//自身坐标系下的矩形范围
    protected scopePolygon: Vec2[];//矩形攻击范围时的多边形
    //缓存对象
    protected cachePos: Vec2;
    protected barrierRect: Rect;//障碍物矩形范围
    /**当前帧是否有攻击效果 */
    public visible: boolean = false;
    public constructor() {
        this.position = v2();
        this._aabb = {
            xMin: 0,
            xMax: 0,
            xScope: 0,
            xCenter: 0,
            yMin: 0,
            yMax: 10000,
            yScope: 10000,
            yCenter: 5000,
            zMin: 0,
            zMax: 0,
            zScope: 0,
            zCenter: 0,
        };
        this.arc = {
            pointLeft: v2(),
            pointRight: v2(),
        };
        this.bordLeftV = v2();
        this.bordRightV = v2();
        this.rect = new Rect();
        this.scopePolygon = [v2(), v2(), v2(), v2()];
        this.cachePos = v2();
        this.barrierRect = new Rect();
        this.visible = false;
    }

    public init() {
        this.visible = false;
    }
    public reset() {
        this.visible = false;
    }

    public setScope(data: {
        atkRole: Role,
        type: GlobalEnum.AtkScopeType,
        radian: number,
        radius?: number,
        halfRadianScope?: number,
        halfWidth?: number,
        length?: number
    }) {
        this.atkRole = data.atkRole;
        this.radian = data.radian;
        if (this.radian < 0) {
            this.radian += pi2;
        }
        this.sin = Math.sin(this.radian);
        this.cos = Math.cos(this.radian);
        this.tan = Math.tan(this.radian);
        this.x = this.atkRole.node.position.x;
        this.y = -this.atkRole.node.position.z;
        this.position.x = this.x;
        this.position.y = this.y;
        this.type = data.type;
        switch (this.type) {
            case GlobalEnum.AtkScopeType.fan: {
                this.radius = data.radius;
                this.radiusSqr = this.radius * this.radius;
                this.halfRadianScope = data.halfRadianScope;
                this.halfSin = Math.sin(this.halfRadianScope);
                this.halfCos = Math.cos(this.halfRadianScope);
                this.halfTan = Math.tan(this.halfRadianScope);

                let rr = this.radian - this.halfRadianScope + pi5;
                this.bordRightV.x = Math.cos(rr);
                this.bordRightV.y = Math.sin(rr);
                let rrl = this.radian + this.halfRadianScope - pi5;
                this.bordLeftV.x = Math.cos(rrl);
                this.bordLeftV.y = Math.sin(rrl);

                let r1 = this.radian + this.halfRadianScope;
                this.arc.pointLeft.x = this.radius * Math.cos(r1) + this.x;
                this.arc.pointLeft.y = this.radius * Math.sin(r1) + this.y;
                let r2 = this.radian - this.halfRadianScope;
                this.arc.pointRight.x = this.radius * Math.cos(r2) + this.x;
                this.arc.pointRight.y = this.radius * Math.sin(r2) + this.y;
                //aabb
                let xMin = this.x;
                let xMax = this.x;
                if (this.checkPointInFan(this.x - this.radius, this.y, this.x, this.y)) {
                    xMin = this.x - this.radius;
                } else {
                    if (xMin > this.arc.pointLeft.x) {
                        xMin = this.arc.pointLeft.x;
                    }
                    if (xMin > this.arc.pointRight.x) {
                        xMin = this.arc.pointRight.x;
                    }
                }
                if (this.checkPointInFan(this.x + this.radius, this.y, this.x, this.y)) {
                    xMax = this.x + this.radius;
                } else {
                    if (xMax < this.arc.pointLeft.x) {
                        xMax = this.arc.pointLeft.x;
                    }
                    if (xMax < this.arc.pointRight.x) {
                        xMax = this.arc.pointRight.x;
                    }
                }
                let zMin = -this.y;
                let zMax = -this.y;
                if (this.checkPointInFan(this.x, this.y + this.radius, this.x, this.y)) {
                    zMin = -this.y - this.radius;
                } else {
                    if (zMin > -this.arc.pointLeft.y) {
                        zMin = -this.arc.pointLeft.y;
                    }
                    if (zMin > -this.arc.pointRight.y) {
                        zMin = -this.arc.pointRight.y;
                    }
                }
                if (this.checkPointInFan(this.x, this.y - this.radius, this.x, this.y)) {
                    zMax = -this.y + this.radius;
                } else {
                    if (zMax < -this.arc.pointLeft.y) {
                        zMax = -this.arc.pointLeft.y;
                    }
                    if (zMax < -this.arc.pointRight.y) {
                        zMax = -this.arc.pointRight.y;
                    }
                }
                this._aabb.xMin = xMin;
                this._aabb.xMax = xMax;
                this._aabb.xScope = (this._aabb.xMax - this._aabb.xMin) * 0.5;
                this._aabb.xCenter = this._aabb.xMin + this._aabb.xScope;
                this._aabb.zMin = zMin;
                this._aabb.zMax = zMax;
                this._aabb.zScope = (this._aabb.zMax - this._aabb.zMin) * 0.5;
                this._aabb.zCenter = this._aabb.zMin + this._aabb.zScope;
                break;
            }
            case GlobalEnum.AtkScopeType.rect: {
                this.halfWidth = data.halfWidth;
                this.length = data.length;
                this.rect.set(0, -this.halfWidth, this.length, this.halfWidth * 2);
                //多边形
                let x = this.halfWidth * this.sin;
                let y = this.halfWidth * this.cos;
                let x1 = this.length * this.cos;
                let y1 = this.length * this.sin;
                this.scopePolygon[0].x = this.x - x;
                this.scopePolygon[0].y = this.y + y;
                this.scopePolygon[1].x = this.x + x1 - x;
                this.scopePolygon[1].y = this.y + y1 + y;
                this.scopePolygon[2].x = this.x + x1 + x;
                this.scopePolygon[2].y = this.y + y1 - y;
                this.scopePolygon[3].x = this.x + x;
                this.scopePolygon[3].y = this.y - y;
                //aabb
                this._aabb.xMin = this.scopePolygon[1].x;
                this._aabb.xMax = this.scopePolygon[1].x;
                this._aabb.zMin = -this.scopePolygon[1].y;
                this._aabb.zMax = -this.scopePolygon[1].y;

                for (let i = 1; i < 4; ++i) {
                    if (this._aabb.xMin > this.scopePolygon[i].x) {
                        this._aabb.xMin = this.scopePolygon[i].x;
                    }
                    if (this._aabb.xMax < this.scopePolygon[i].x) {
                        this._aabb.xMax = this.scopePolygon[i].x;
                    }

                    if (this._aabb.zMin > -this.scopePolygon[i].y) {
                        this._aabb.zMin = -this.scopePolygon[i].y;
                    }
                    if (this._aabb.zMax < -this.scopePolygon[i].y) {
                        this._aabb.zMax = -this.scopePolygon[i].y;
                    }
                }
                this._aabb.xScope = (this._aabb.xMax - this._aabb.xMin) * 0.5;
                this._aabb.xCenter = this._aabb.xMin + this._aabb.xScope;
                this._aabb.zScope = (this._aabb.zMax - this._aabb.zMin) * 0.5;
                this._aabb.zCenter = this._aabb.zMin + this._aabb.zScope;
                break;
            }
        }
    }

    public getAABB() {
        return this._aabb;
    }
    public checkAABB(target) {
        if (this.atkRole.Id == target.Id) return false;
        let a1 = this._aabb;
        let a2 = target.getAABB();
        return a1.xMax >= a2.xMin && a1.xMin <= a2.xMax
            && a1.yMax >= a2.yMin && a1.yMin <= a2.yMax
            && a1.zMax >= a2.zMin && a1.zMin <= a2.zMax;
    }

    public onColled(coller) {
        switch (coller.collerType) {
            case GlobalEnum.CollerType.role: {
                this.colledRole(coller);
                break;
            }
            case GlobalEnum.CollerType.barrier: {
                this.colledBarrier(coller);
                break;
            }
        }
    }
    protected audioClip: string = null;
    public processHitAudio() {
        if (!!this.audioClip && this.atkRole.disToPlayerSqr < GameConfig.GameRule.enemyAudioScopeSqr) {
            this.playAudio(this.audioClip);
        }
        this.audioClip = null;
    }
    protected hitSuccess(other) {
        this.atkRole.hitSuccess(other);
        other.beHit(this.atkRole);
        if (this.atkRole.isPlayer && other.collerType == GlobalEnum.CollerType.barrier) {
            switch (other.breakAudioType) {
                case 0: {
                    this.audioClip = GlobalEnum.AudioClip.breakWood;
                    break;
                }
                case 1: {
                    this.audioClip = GlobalEnum.AudioClip.breakStones;
                    break;
                }
                case 2: {
                    this.audioClip = GlobalEnum.AudioClip.breakMetal;
                    break;
                }
            }
        }
    }
    protected playAudio(clip: string) {
        EventManager.emit(EventType.Common.AudioEvent.playEffect, clip);
    }

    //初筛碰到了角色
    protected colledRole(role: Role) {
        if (role.invicible || !role.isAlive) return;
        switch (this.type) {
            case GlobalEnum.AtkScopeType.rect: {
                this.checkRoleRect(role);
                break;
            }
            case GlobalEnum.AtkScopeType.fan: {
                this.checkRoleFan(role);
                break;
            }
        }
    }
    protected checkRoleRect(role: Role) {
        let aabb = role.getAABB();
        let r = aabb.xScope;
        let x = aabb.xCenter - this.x;
        let y = -aabb.zCenter - this.y;
        this.cachePos.x = x * this.cos + y * this.sin;
        this.cachePos.y = y * this.cos - x * this.sin;
        if (Intersection2D.rectCircle(this.rect, this.cachePos, r)) {
            this.hitSuccess(role);
        }
    }
    protected checkRoleFan(role: Role) {
        //圆与扇形检测
        let aabb = role.getAABB();
        //圆心在扇形范围内
        if (this.checkPointInFan(aabb.xCenter, -aabb.zCenter, this.x, this.y, this.radiusSqr)) {
            this.hitSuccess(role);
            return;
        }
        //圆心在扇形附近且圆与扇形相交
        let r = aabb.xScope;
        let l = r / this.halfSin;
        let cx = this.x - l * this.cos;
        let cy = this.y - l * this.sin;
        let v = this.radius + r + l;
        v *= v;
        if (this.checkPointInFan(aabb.xCenter, -aabb.zCenter, cx, cy, v)) {
            let rr = l - r;
            rr = r * r;
            let x = aabb.xCenter - cx;
            let y = -aabb.zCenter - cy;
            if (x * x + y * y < rr) {
                return;
            }
            this.hitSuccess(role);
            return;
        }
    }

    //初筛碰到了障碍物
    protected colledBarrier(barrier) {
        switch (this.type) {
            case GlobalEnum.AtkScopeType.rect: {
                this.checkBarrierRect(barrier);
                break;
            }
            case GlobalEnum.AtkScopeType.fan: {
                this.checkBarrierFan(barrier);
                break;
            }
        }
    }
    protected checkBarrierRect(barrier: Barrier) {
        //矩形与倾斜矩形
        let aabb = barrier.getAABB();
        this.barrierRect.set(aabb.xMin, -aabb.zMax, aabb.xMax - aabb.xMin, aabb.zMax - aabb.zMin);
        if (Intersection2D.rectPolygon(this.barrierRect, this.scopePolygon)) {
            this.hitSuccess(barrier);
        }
    }
    protected checkBarrierFan(barrier: Barrier) {
        let aabb = barrier.getAABB();
        this.barrierRect.set(aabb.xMin, -aabb.zMax, aabb.xMax - aabb.xMin, aabb.zMax - aabb.zMin);
        //扇形的边是否与矩形相交
        if (Intersection2D.lineRect(this.position, this.arc.pointLeft, this.barrierRect)) {
            this.hitSuccess(barrier);
            return;
        }
        if (Intersection2D.lineRect(this.position, this.arc.pointRight, this.barrierRect)) {
            this.hitSuccess(barrier);
            return;
        }
        //矩形是否有顶点在扇形范围内
        if (this.checkPointInFan(this.barrierRect.x, this.barrierRect.y, this.x, this.y, this.radiusSqr)
            || this.checkPointInFan(this.barrierRect.x, this.barrierRect.yMax, this.x, this.y, this.radiusSqr)
            || this.checkPointInFan(this.barrierRect.xMax, this.barrierRect.y, this.x, this.y, this.radiusSqr)
            || this.checkPointInFan(this.barrierRect.xMax, this.barrierRect.yMax, this.x, this.y, this.radiusSqr)) {
            this.hitSuccess(barrier);
            return;
        }
        //矩形的边是否与圆弧有两个交点
        if (this.checkLineHCrossFan(this.barrierRect.x, this.barrierRect.xMax, this.barrierRect.y)
            || this.checkLineHCrossFan(this.barrierRect.x, this.barrierRect.xMax, this.barrierRect.yMax)
            || this.checkLineVCrossFan(this.barrierRect.x, this.barrierRect.y, this.barrierRect.yMax)
            || this.checkLineVCrossFan(this.barrierRect.xMax, this.barrierRect.y, this.barrierRect.yMax)) {
            this.hitSuccess(barrier);
            return;
        }
    }
    //检测点是否在扇形范围内
    protected checkPointInFan(x: number, y: number, cx: number, cy: number, rSqr?: number): boolean {
        x -= cx;
        y -= cy;
        if (!!rSqr && x * x + y * y > rSqr) {
            return false;
        }
        return x * this.bordLeftV.x + y * this.bordLeftV.y > 0
            && x * this.bordRightV.x + y * this.bordRightV.y > 0;
    }
    //极限情况：垂直边与圆弧有两个交点
    protected checkLineVCrossFan(x: number, y0: number, y1: number): boolean {
        let y = x - this.x;
        y *= y;
        if (this.radiusSqr < y) return false;
        y = Math.sqrt(this.radiusSqr - y);
        let yMin = this.y - y;
        if (yMin > y1) return false;
        let yMax = this.y + y;
        if (yMax < y0) return false;
        if (yMin < y0 && yMax > y1) return false;
        return this.checkPointInFan(x, this.y + y, this.x, this.y)
            || this.checkPointInFan(x, this.y - y, this.x, this.y);
    }
    protected checkLineHCrossFan(x0: number, x1: number, y: number) {
        let x = y - this.y;
        x *= x;
        if (this.radiusSqr < x) return false;
        x = Math.sqrt(this.radiusSqr - x);
        let xMin = this.x - x;
        if (xMin > x1) return false;
        let xMax = this.x + x;
        if (xMax < x0) return false;
        if (xMin < x0 && xMax > x1) return false;
        return this.checkPointInFan(this.x + x, y, this.x, this.y)
            || this.checkPointInFan(this.x - x, y, this.x, this.y);
    }
}
